#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 引入MQTT客户端库头文件，用于使用MQTT相关功能
#include "MQTTClient.h"

// 定义MQTT服务器地址
#define ADDRESS "tcp://192.168.33.33:1883"
// 定义MQTT客户端唯一标识符
#define CLIENTID "b253ba38-daf6-4b37-984f-5d8fdc6a1cfa"
// 定义订阅主题名称
#define TOPIC_PULL "pull"
// 定义发布主题名称
#define TOPIC_PUSH "push"
// 定义要发布的消息内容
#define PAYLOAD "Hello World!"
// 定义MQTT服务质量等级
#define QOS 1
// 定义操作超时时间，单位为毫秒
#define TIMEOUT 10000L

// 静态初始化一个MQTT消息结构体，用于后续发布消息
static MQTTClient_message pubmsg = MQTTClient_message_initializer;

/**
 * @brief 消息发送确认回调函数
 *
 * 当消息成功发送到MQTT服务器时，该函数会被调用。
 *
 * @param context 回调上下文，一般为NULL
 * @param dt 消息发送的令牌，用于唯一标识一条消息
 */
void delivered(void *context, MQTTClient_deliveryToken dt)
{
    // 打印消息发送确认信息，包含令牌值
    printf("Message with token value %d delivery confirmed\n", dt);
}

/**
 * @brief 消息到达回调函数
 *
 * 当客户端接收到订阅主题的消息时，该函数会被调用。
 *
 * @param context 回调上下文，一般为NULL
 * @param topicName 消息所在的主题名称
 * @param topicLen 主题名称的长度
 * @param message 接收到的MQTT消息结构体指针
 * @return int 固定返回1，表示消息处理完成
 */
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
    // 打印消息到达提示
    printf("Message arrived\n");
    // 打印消息所在的主题名称
    printf("   topic: %s\n", topicName);
    // 打印接收到的消息内容
    printf("  message: %.*s\n", message->payloadlen, (char *)message->payload);
    // 释放消息结构体占用的内存
    MQTTClient_freeMessage(&message);
    // 释放主题名称占用的内存
    MQTTClient_free(topicName);
    // 返回1表示消息处理完成
    return 1;
}

/**
 * @brief 连接丢失回调函数
 *
 * 当客户端与MQTT服务器的连接断开时，该函数会被调用。
 *
 * @param context 回调上下文，一般为NULL
 * @param cause 连接丢失的原因描述
 */
void connlost(void *context, char *cause)
{
    // 打印连接丢失提示
    printf("\nConnection lost\n");
    // 打印连接丢失的原因
    printf("   cause: %s\n", cause);
}

int main(int argc, char *argv[])
{
    // 定义MQTT客户端对象
    MQTTClient client;
    // 初始化MQTT连接选项结构体
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    // 定义返回码变量，用于存储各个操作的结果
    int rc;

    // 1. 创建MQTT客户端实例
    if ((rc = MQTTClient_create(&client, ADDRESS, CLIENTID,
                                MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTCLIENT_SUCCESS)
    {
        // 若创建失败，打印错误信息
        printf("Failed to create client, return code %d\n", rc);
        // 设置返回码为程序执行失败
        rc = EXIT_FAILURE;
        // 跳转到程序退出部分
        goto exit;
    }

    // 2. 设置MQTT客户端的回调函数
    if ((rc = MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered)) != MQTTCLIENT_SUCCESS)
    {
        // 若设置失败，打印错误信息
        printf("Failed to set callbacks, return code %d\n", rc);
        // 设置返回码为程序执行失败
        rc = EXIT_FAILURE;
        // 跳转到销毁客户端并退出的部分
        goto destroy_exit;
    }

    // 3. 连接到MQTT服务器
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        // 若连接失败，打印错误信息
        printf("Failed to connect, return code %d\n", rc);
        // 设置返回码为程序执行失败
        rc = EXIT_FAILURE;
        // 跳转到销毁客户端并退出的部分
        goto destroy_exit;
    }

    // 打印订阅信息，提示用户按Q键退出
    printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n"
           "Press Q<Enter> to quit\n\n",
           TOPIC_PULL, CLIENTID, QOS);
    // 4. 订阅指定主题
    if ((rc = MQTTClient_subscribe(client, TOPIC_PULL, QOS)) != MQTTCLIENT_SUCCESS)
    {
        // 若订阅失败，打印错误信息
        printf("Failed to subscribe, return code %d\n", rc);
        // 设置返回码为程序执行失败
        rc = EXIT_FAILURE;
    }
    else
    {
        int ch;
        do
        {
            // 读取用户输入的字符
            ch = getchar();
        } while (ch != 'Q' && ch != 'q');

        // 取消订阅指定主题
        if ((rc = MQTTClient_unsubscribe(client, TOPIC_PULL)) != MQTTCLIENT_SUCCESS)
        {
            // 若取消订阅失败，打印错误信息
            printf("Failed to unsubscribe, return code %d\n", rc);
            // 设置返回码为程序执行失败
            rc = EXIT_FAILURE;
        }
    }

    // 设置要发布的消息内容
    pubmsg.payload = PAYLOAD;
    // 设置消息的长度
    pubmsg.payloadlen = (int)strlen(PAYLOAD);
    // 设置消息的服务质量等级
    pubmsg.qos = QOS;
    // 发布消息到指定主题
    if ((rc = MQTTClient_publishMessage(client, TOPIC_PUSH, &pubmsg, NULL)) != MQTTCLIENT_SUCCESS)
    {
        // 若发布失败，打印错误信息
        printf("Failed to publish message, return code %d\n", rc);
        // 设置返回码为程序执行失败
        rc = EXIT_FAILURE;
    }

    // 断开与MQTT服务器的连接
    if ((rc = MQTTClient_disconnect(client, 10000)) != MQTTCLIENT_SUCCESS)
    {
        // 若断开连接失败，打印错误信息
        printf("Failed to disconnect, return code %d\n", rc);
        // 设置返回码为程序执行失败
        rc = EXIT_FAILURE;
    }
    // 返回操作结果
    return rc;

destroy_exit:
    // 销毁MQTT客户端实例
    MQTTClient_destroy(&client);
exit:
    // 返回操作结果
    return rc;
}